「我們是認真嚴肅地看待命名這件事,請您牢記這一點」
取自: Clean Code (p.20)
我們替 jar、war、ear、json...等檔案不斷地命名再命名...
這是一個專案的目錄結構:
code/
├─ a/
├─ b/
│ ├─ A
│ ├─ B
├─ c/
├─ d.x/
├─ d.y/
├─ d.z/
有可能從這樣的命名結構看出上列專案在做什麼嗎? 恐怕是很困難的
實務上雖不至於有人會這麼胡亂地命名,但由於對命名 (Naming) 的小看,導致還沒點開程式碼,專案本身的可讀性就先下降了,這種情況倒是屢見不鮮...
接著將上面的目錄結構改為:
code/
├─ lib/
├─ machine/
│ ├─ mipssim
│ ├─ console
├─ network/
├─ build.cygwin/
├─ build.linux/
├─ build.macosx/
我想每個讀者即使不了解作業系統,也能從架構中大概猜到這是一個:
好的命名使讀者在尚未深入細節前,就能對專案的模塊、類別的定位、函式行為...等有初步的概念
書中作者假設這是常識了,連提都不提 QQ
現代軟體開發基本都是採用下列命名慣例:
string firstName = "JJJJ";
string userPhone = "123456789"
public class HomeController : Controller
{
public HomeController()
{
}
public IActionResult Index()
{
return View();
}
}
# 切分訓練資料集
array = data.values
X = array[:, :-1]
Y = array[:, -1].astype('int')
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=0)
例如:
int d; // elapsed time in days
// vs.
int elapsedTimeInDays;
int daysSinceCreation;
int fileAgeInDays;
哪一種命名更有表達力呢?
若這些變數是從外部環境引入(import, using),不實際點開檔案前,有誰會知道 "xxx.d" 代表什麼呢?
「如果一個變數名稱還需要註解的輔助,那麼這個名稱就不具備展現意圖的能力」
「避開使用那些與原意圖相違背的常見單詞」
def get_dist(a1, a2):
return a2 - a1
# Which Do you Like?
def get_dist(source, destination):
return destination - source
P.S. 筆者真的有在工作場合看過取名為 List1, List2, List3 這樣的程式碼...,也許改成ListForXXX, ListForYYY, ListForZZZ 會是更好一點的作法
說明:
當全域搜尋某變數時,我們不會希望跳出一大堆與目標無關的內容,只因他們的名稱太類似、甚至一樣 (白話文: 菜市場名)。在命名的時都要問自己: 如果未來想搜尋這個名稱、是否能馬上就找到?
不用擔心影響到打字時間,現代開發工具補齊功能都很完善
例: 如果我們未來想搜尋 "34"、"5" 出現在哪,試問專案搜尋結果會出現幾種可能性?
int s = 0;
for (int i = 0; i < 34; ++i){
s += t[i] / 5;
}
而我們多宣告了 2 個變數,就能大大地減少變數搜尋時間:
const int NUMBER_OF_TASKS = 34;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int i = 0; i < NUMBER_OF_TASKS; ++i){
sum += tasks[i] / WORK_DAYS_PER_WEEK;
}
「就這一點而言,長命名勝過短命名」
class Employee
{
public:
void setEmployeeName(string empName);
string getEmployeeName() const;
void setEmployeeId(string empId);
int getEmployeeId() const;
bool isEmployeeNameValid();
bool isEmployeeIdDuplicate();
private:
private string employeeName;
private int employeeId;
}
「替單一抽象概念挑選一個字詞,並堅持持續地使用它」
「我們需要註解,因為我們無法每次都找到不使用註解就能表達意圖的方法,但使用註解並不值得慶賀」
取自: Clean Code (p.62)
算一下你花了幾秒才理解這段 Code 的意義?
// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) &&
(employee.age > 65))
{
// Do Something
}
else
{
// Do Something
}
從上面的 Code 我們可觀察到:
接著看看下面的 Code (筆者針對 Clean Code p.63 進行的補充,如有不足敬請指點)
if (employee.isEligibleForFullBenefits())
{
// Do Something
}
else
{
// Do Something
}
bool isEligibleForFullBenefits()
{
bool isApplyRetire = employee.flags & HOURLY_FLAG;
bool isEligibleForRetire = employee.age > RETIRE_AGE_LIMIT;
return isApplyRetire && isEligibleForRetire;
}
NOTE: 某一些程式語言 (例如 C#) 可能會習慣在每一個 Method 或 Variables 之上寫些 Doc 型註解
/// <summary>
/// To calculate total salary.
/// </summary>
/// <param name="salary"></param>
/// <param name="bonus"></param>
public void GetEmployeeSalary( int salary, int bonus)
{
int totalSalary = salary + bonus;
return totalSalary;
}
這種情形建議還是遵照慣例會比較妥當,以利後續 API 文件的自動產生法律型註解
// utility.cc
// Debugging routines. Allows users to control whether to
// print DEBUG statements, based on a command line argument.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "utility.h"
但如果可能,這樣的註解內容不應直接是契約或條款,建議讓它去參考一個外部文件
對意圖的解釋 & 後果的告誡
currentThread->Finish(); // NOTE: if the procedure "main"
// returns, then the program "nachos"
// will exit (as any other normal program
// would). But there may be other
// threads on the ready list. We switch
// to those threads by saying that the
// "main" thread is finished, preventing
// it from returning.
return(0); // Not reached...
上述的註解特別提醒了 Thread 完成後需要注意的事項,尤其我們可以知道 return 是不會馬上被觸發到的
TODO (待辦事項) 註解
大部分的 IDE 都提供 Task Tags 查找的機制來找出所有的 TODO 註解。建議定期地審視這些待辦事項,並且刪除已經不再需要的待辦事項
衍生閱讀: 特殊註解:TODO、FIXME、XXX
誤導型的註解
絕對不要寫下錯誤的註解!!!
被註解起來的程式碼
這是很討人厭的,別這樣做!!!
日誌型註解 & 出處和署名
現代版控工具都很發達,別再用註解寫日誌
多餘的註解
別讓讀註解比讀程式碼還費時
其他
位置標誌 (效果非常顯著時才使用,否則只是凌亂物)
{
// code...
// Actions //////////////////////////
// code...
}
右大括號後面的註解
while (...)
{
// code...
} // end while
「程式的編排是很重要的。編排是一種溝通方式,而溝通是專業開發者的首要之務」
取自: Clean Code (p.86)
/* Eaxmple 1 */
(-b+Math.sqrt(determinant))/(2*a);
// vs.
(-b + Math.sqrt(determinant)) / (2*a);
/* Eaxmple 2 */
return b*b-4*a*c;
// vs.
return b*b - 4*a*c;